﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using My.SharePoint.STSADM.Commands.OperationHelpers;
using System.Collections.Specialized;
using Microsoft.SharePoint;
using System.IO;
using System.Xml;
using System.Data;
using System.Data.Linq;
using Microsoft.SharePoint.Utilities;
using System.Net;


namespace IdeasFree.SharePoint.StsAdm.Commands.Helpers
{
    internal partial class Operation
    {
        internal static void ExportDocument(SPParamCollection Params, System.Collections.Specialized.StringDictionary keyValues)
        {
            string url = Params["url"].Value;
            string doclib = Params["doclib"].Value;
            string exportFolder = Params["exportfolder"].Value;
            bool isMetaOnly;
            bool.TryParse(Params["metaonly"].Value, out isMetaOnly);
            bool isWebService;
            bool.TryParse(Params["enablews"].Value, out isWebService);
            string excludeFolder = Params["excludefolder"].Value;
            List<string> excludeFolderList = new List<string>();
            if (excludeFolder != null)
            {
                excludeFolderList = excludeFolder.Split(',').ToList<string>();
            }

            if (isWebService)
            {
                BaseExportDocument ops = new ExportDocumentWSOperation();
                ops.ProcessData(0, doclib, exportFolder, url, excludeFolderList, isMetaOnly);
            }
            else
            {
                BaseExportDocument ops = new ExportDocumentOperation();
                ops.ProcessData(0, doclib, exportFolder, url, excludeFolderList, isMetaOnly);
            }
        }
    }

    public abstract class BaseExportDocument
    {
        abstract public int ProcessData(int startNum, string doclib, string exportFolder, string url, List<string> excludeFolderList, bool metaOnly);
        virtual internal void ExtractMetadata(Dictionary<string, string> metaDictionary, string xmlFile)
        {
            Console.WriteLine("  Extracting metadata {0}", xmlFile);
            ListMeta listMeta = new ListMeta();
            ListMetaListItem listMetaItem = new ListMetaListItem();

            try
            {
                foreach (string key in metaDictionary.Keys)
                {
                    listMetaItem.SetAttribute(key.Replace(" ", ""), metaDictionary[key]);
                }
            }
            catch
            { }

            listMeta.ListItem = listMetaItem;
            listMeta.SaveToFile(xmlFile);
        }
        // Break apart the URL and make a File System directory heirarchy from it if needed.
        /// <summary>
        /// Creates a folder at strExportPath if it doesn't exist
        /// </summary>
        /// <param name="strExportPath">The File System Path (Directory) create the folder in</param>
        /// <param name="folder">The SPFolder for which we are creating the file system folder for.</param>
        /// <returns></returns>
        virtual internal string CheckPathAndCreate(string strExportPath, string strSrcPath)
        {
            string strPath = Path.Combine(strExportPath, strSrcPath.Replace("/", "\\").TrimStart('\\'));
            System.IO.Directory.CreateDirectory(strPath);

            return strPath;
        }
    }
    public class ExportDocumentOperation : BaseExportDocument
    {
        public ExportDocumentOperation()
        {

        }

        private void ReadWriteStream(Stream readStream, Stream writeStream)
        {
            int Length = 256;
            Byte[] buffer = new Byte[Length];
            int bytesRead = readStream.Read(buffer, 0, Length);
            // write the required bytes
            while (bytesRead > 0)
            {
                writeStream.Write(buffer, 0, bytesRead);
                bytesRead = readStream.Read(buffer, 0, Length);
            }
            readStream.Close();
            writeStream.Close();
        }

        private void ExtractMetaData(SPListItem item, string xmlFile)
        {
            Dictionary<string, string> dataRowEntry = new Dictionary<string, string>();
            dataRowEntry.Add("ListName", item.ParentList.Title);

            foreach (SPField fld in item.Fields)
            {
                if (!(fld.Hidden))
                {
                    dataRowEntry.Add(fld.InternalName, fld.GetFieldValueAsText(item[fld.InternalName]));
                }
            }
            ExtractMetadata(dataRowEntry, xmlFile);
        }

        private long ExtractFile(SPFolder folder, List<string> excludeFolderList, string strExportPath,bool metaOnly)
        {

            Console.WriteLine("Processing Folder " + folder.ServerRelativeUrl);
            string strPath = null;
            long lFolderSize = 0;

            // If we're exporting the files from the web to the local file system, 
            //  create the appropriate file system folder
            if (!excludeFolderList.Contains(folder.Name))
            {
                if (strExportPath != null)
                {
                    strPath = CheckPathAndCreate(strExportPath, folder);
                }

                foreach (SPFile file in folder.Files)
                {
                    string sFileLoc = Path.Combine(strPath, file.Name);//+ "\\" + file.Name;
                    string sFileMetaLoc = Path.Combine(strPath, file.Name + ".xml");

                    if (!metaOnly)
                    {
                        Console.WriteLine("    Exporting file " + file.Name + " to " + sFileLoc);
                        FileStream fs = new FileStream(sFileLoc, FileMode.OpenOrCreate, FileAccess.Write);
                        ReadWriteStream(file.OpenBinaryStream(), fs);
                    }
                    SPListItem fileItem = file.Item;
                    ExtractMetaData(fileItem, sFileMetaLoc);
                }
            }

            foreach (SPFolder subfolder in folder.SubFolders)
                if (!excludeFolderList.Contains(folder.Name))
                    ExtractFile(subfolder, excludeFolderList, strExportPath, metaOnly);
                else
                    Console.WriteLine("Skipping folder : {0}", folder.Name);
            return lFolderSize;
        }


        private string CheckPathAndCreate(string strExportPath, SPFolder folder)
        {
            return base.CheckPathAndCreate(strExportPath, folder.Url);
        }

        public override int ProcessData(int startNum, string doclib, string exportFolder, string url, List<string> excludeFolderList, bool metaOnly)
        {
            using (SPSite spSite = new SPSite(url))
            {
                using (SPWeb spWeb = spSite.OpenWeb())
                {
                    spWeb.Lists.IncludeRootFolder = true;
                    SPList spList = spWeb.Lists[doclib];
                    ExtractFile(spList.RootFolder, excludeFolderList, exportFolder,metaOnly);
                }
            }
            return 1;
        }

    }
    public class ExportDocumentWSOperation : BaseExportDocument
    {
        private ICredentials _credentials;

        public ExportDocumentWSOperation()
        {
            _credentials = CredentialCache.DefaultCredentials;
        }
        public ExportDocumentWSOperation(ICredentials credential)
        {
            _credentials = credential;
        }

        private void ExtractMetaData(DataRow dr, string pathLocation)
        {
            Dictionary<string, string> dataRowEntry = new Dictionary<string, string>();

            foreach (DataColumn dc in dr.Table.Columns)
                if (dr[dc.ColumnName] == null)
                    continue;
                else
                    dataRowEntry.Add(dc.ColumnName, dr[dc.ColumnName].ToString());

            string fileName = SPEncode.UrlDecodeAsUrl(dr["ows_LinkFileName"].ToString());

            ExtractMetadata(dataRowEntry, Path.Combine(pathLocation,fileName + ".xml"));
        }
        public override int ProcessData(int startNum, string doclib, string exportFolder, string url, List<string> excludeFolderList, bool metaOnly)
        {
            ListWS.Lists listService = new ListWS.Lists();
            listService.Credentials = _credentials;
            listService.Url = url.TrimEnd('/') + "/_vti_bin/lists.asmx";

            Console.WriteLine("Processing {0} - {1}", url, doclib);
            return ProcessData(0, doclib, exportFolder, url, metaOnly, listService);
        }
        private int ProcessData(int startNum, string doclib, string exportFolder, string url, bool metaOnly, ListWS.Lists listService)
        {
            
            XmlDocument xmlDoc = new System.Xml.XmlDocument();
            XmlNode listQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
            XmlNode listViewFields = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            XmlNode listQueryOptions = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
            listQueryOptions.InnerXml = "<ViewAttributes Scope=\"Recursive\" />";

            listQuery.InnerXml = String.Format(@"
                                       <Where>
                                          <Gt>
                                             <FieldRef Name='ID' />
                                             <Value Type='Counter'>{0}</Value>
                                          </Gt>
                                       </Where>
                                       <OrderBy>
                                          <FieldRef Name='ID' />
                                       </OrderBy>", startNum);

            
            System.Xml.XmlNode items = listService.GetListItems(doclib, null,
                                                                listQuery, listViewFields, 
                                                                null, listQueryOptions, null);

            XmlNodeReader reader = new XmlNodeReader(items);
            DataSet ds = new DataSet();
            ds.ReadXml(reader);
            int maxRows = 0;
            if (ds.Tables.Count < 2)
                goto Check;

            DataTable dt = ds.Tables[1];

            if (dt.Rows.Count > 0)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    
                    if (dr["ows_EncodedAbsUrl"] == null || dr["ows_LinkFileName"] == null)
                        continue;
                    string absoluteUrl = SPEncode.UrlDecodeAsUrl(dr["ows_EncodedAbsUrl"].ToString());
                    string fileName = SPEncode.UrlDecodeAsUrl(dr["ows_LinkFileName"].ToString());
                    string path = absoluteUrl.Remove(absoluteUrl.Length - fileName.Length).Remove(0, url.TrimEnd('/').Length);
                    
                    if (!metaOnly)
                        ExtractFile(dr, CheckPathAndCreate(exportFolder, path));

                    ExtractMetaData(dr, CheckPathAndCreate(exportFolder, path));
                }
                int.TryParse(dt.Rows[dt.Rows.Count - 1]["ows_ID"].ToString(), out maxRows);
            }
        Check:
            if (maxRows > 0)
                goto Repro;

            return maxRows;

        Repro:
            maxRows = ProcessData(maxRows, doclib, exportFolder, url, metaOnly, listService);
            goto Check;
        }
        private void ExtractFile(DataRow dr, string pathLocation)
        {
            string absoluteUrl = SPEncode.UrlDecodeAsUrl(dr["ows_EncodedAbsUrl"].ToString());
            string fileName = SPEncode.UrlDecodeAsUrl(dr["ows_LinkFileName"].ToString());

            Console.WriteLine("  Extracting {0} to {1}", absoluteUrl, pathLocation);
            WebClient wc = new WebClient();
            wc.Credentials = _credentials;
            wc.DownloadFile(absoluteUrl, Path.Combine(pathLocation, fileName));

        }

    }
}
